#!/usr/bin/python

# Possum Websync
#
# A simple yet efficient script to keep your local directory 
# synchronized with your FTP server
# 
# By Jackson Yee
# Copyright 2007
#
# This program is released under the terms of the GPL version 2
# available at http://www.gnu.org/copyleft/gpl.html
#
# Feel free to modify or distribute, but please send me some patches
# if you add any features.

import sys
import getpass
import possum
import libsync
import time

AppName = 'Possum Websync'
AppVersion = '2007-06-19'

# =====================================================================
def PrintUsage():
	print """Usage: possum-websync [options] sourcedir destdir ignoredirs

sourcedir and destdir can be either local directories or FTP
directories specified in the form ftp://username@server:path
A password will be automatically prompted.

ignoredirs is an optional list of regular expressions. If a directory
matches any one of these expressions, it will be skipped in the copying
process.

Options are as follows:
  -c	Copy: updates destination with newer/nonexistent files
      from source
  -s  Sync (default): Updates both source and destination with newer 
      files from each
  -d  Delete: Updates destination with newer/nonexistent files from
      from source. In addition, all files which exist on the
      destination but not on the source will be deleted.
  -a  Automatically copy files without prompting for continuation
      after updates are calculated
  -q  Quiet: Don't output any updates as to progress. If you set
      this flag, automatic will be set as well since it's rather
      pointless to decide on updates that you can't see.
  -p	Clear saved password and use new password (if you have changed
      the password for your FTP user on the server)
"""		

# =====================================================================
def StripTrailingSlash(s):
	if len(s) > 1:
		if s[-1] == '/':
			return s[0:-1]
	
	return s	
	
# =====================================================================
def ParseDirectories(args):
	source = {}
	dest = {}
	ignore = args[2:]
	
	if args[0].startswith('ftp://'):
		parts = args[0].replace('ftp://', '').split('@')
		
		if len(parts) < 2:
			print 'Error: You must specify a username for the FTP host'
			return None, None, None
		
		source['username'] = parts[0]
		pos = parts[1].find('/')
		
		if pos == -1:
			print 'Error: You must specify a path after the hostname'
			return None, None, None
			
		source['host'] = parts[1][0:pos]
		source['path'] = StripTrailingSlash(parts[1][pos:])
		dest['path'] = StripTrailingSlash(args[1])
	elif args[1].startswith('ftp://'):
		parts = args[1].replace('ftp://', '').split('@')
		
		if len(parts) < 2:
			print 'Error: You must specify a username for the FTP host'
			return None, None, None
		
		dest['username'] = parts[0]
		pos = parts[1].find('/')
		
		if pos == -1:
			print 'Error: You must specify a path after the hostname'
			return None, None, None
			
		dest['host'] = parts[1][0:pos]
		dest['path'] = StripTrailingSlash(parts[1][pos:])
		source['path'] = StripTrailingSlash(args[0])
	else:
		source['path'] = StripTrailingSlash(args[0])
		dest['path'] = StripTrailingSlash(args[1])
		
	return source, dest, ignore
	
# =====================================================================
def Run():
	conf = possum.Config(AppName)
	conf.Load()
		
	if len(sys.argv) < 3:
		PrintUsage()
		return
	
	mode = libsync.FileSync.SYNC
	automatic = False
	quiet = False
	logtofile = False
	hasoptions = False
	passwordsaved = True
	
	if len(sys.argv) > 3:
		if len(sys.argv[1]) > 1:
			if sys.argv[1][0] == '-':
				if 'c' in sys.argv[1]:
					mode = libsync.FileSync.COPY
				
				if 'd' in sys.argv[1]:
					mode = libsync.FileSync.DELETE
					
				if 'a' in sys.argv[1]:
					automatic = True
					
				if 'q' in sys.argv[1]:
					quiet = True
					automatic = True
					
				if 'l' in sys.argv[1]:
					logtofile = True
					
				if 'p' in sys.argv[1]:
					passwordsaved = False
				
				hasoptions = True
		
	if hasoptions:
		source, dest, ignore = ParseDirectories(sys.argv[2:])
	else:
		source, dest, ignore = ParseDirectories(sys.argv[1:])
	
	if not source:
		return
	
	if source.has_key('host'):
		if passwordsaved:
			password = conf.get( source['username'] + '@' + source['host'] )
		
		if not password:
			passwordsaved = False
			password = getpass.getpass('Password for %s@%s: ' % (source['username'], source['host']) )
		print 'Connecting...'
		f = libsync.FTPSync(source['host'], source['username'], password)
		f.SetDownload(True)
		
		if not passwordsaved:
			r = raw_input('Do you wish to save this password? (Type "y" or "n" and press enter): ')
			if 'y' in r or 'Y' in r:
				conf[ source['username'] + '@' + source['host'] ] = password
	elif dest.has_key('host'):
		if passwordsaved:
			password = conf.get( dest['username'] + '@' + dest['host'] )
		
		if not password:
			passwordsaved = False
			password = getpass.getpass('Password for %s@%s: ' % (dest['username'], dest['host']) )
		print 'Connecting...'
		f = libsync.FTPSync(dest['host'], dest['username'], password)
		f.SetDownload(False)
		
		if not passwordsaved:
			r = raw_input('Do you wish to save this password? (Type "y" or "n" and press enter): ')
			if 'y' in r or 'Y' in r:
				conf[ dest['username'] + '@' + dest['host'] ] = password
	else:
		f = libsync.FileSync()
	
	conf.Save()
	
	if quiet:
		f.log = possum.NullLog
	elif logtofile:
		f.log = possum.FileLog('possum-websync.log')
		
	f.mode = mode
	f.Scan(source['path'], dest['path'], ignore)
	
	while f.sourcethread.isAlive() or f.destthread.isAlive():
		try:
			time.sleep(1)
		except KeyboardInterrupt, e:
			print 'Cancelling sync...'
			f.Cancel()
	
	if f.cancel:
		return
	
	f.CalculateUpdates()
	
	if not len(f.sourcefiles) and not len(f.destfiles) and not len(f.destfilestodelete) and not len(f.destdirstodelete):
		print 'Everything already in sync. Nothing to do.'
		return
	
	if not automatic:
		r = raw_input('Do you wish to continue? (Type "y" or "n" and press enter): ')
		if 'y' in r or 'Y' in r:
			print 'Syncing'
			f.Sync()
		else:
			print 'Canceling sync'
	else:
		f.Sync()
	
if __name__ == '__main__':
	Run()

